package me.spring.cloud.common.components.ips.maker;


import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantLock;

public class QQwry2TxtMaker {
  private File qqwryFile;
  private static ReentrantLock lock = new ReentrantLock();
  private byte[] data;
  private long firstIndexOffset;
  private long lastIndexOffset;
  private long totalIndexCount;
  private static final long IP_RECORD_LENGTH = 10;
  private static String unknowStr = "未知";
  private static String[] citys = new String[]{"北京","天津","内蒙古","上海","广西","西藏","宁夏","新疆","香港","澳门","重庆"};

  private static Map<String,String[]> unknowMap = new HashMap<String,String[]>(){{put(unknowStr,
      new String[]{unknowStr,unknowStr,unknowStr,unknowStr,unknowStr});}};
  private static Entry<String,String[]> unknowEntry = unknowMap.entrySet().stream().findFirst().get();

  /**
   * 全世界国家、省、市、区信息表
   */
  private File globalRegionFile = null;
  private Map<Integer, Map<String,String[]>> groupRegionMap = null;
  private Map<String, String[]> globalRegionMap = null;

  public QQwry2TxtMaker(String filePath,String globalRegionFile) throws Exception {
    this.qqwryFile = new File(filePath);
    this.globalRegionFile= new File(globalRegionFile);
    loadCsv();
    loadTxt();
  }

  private void loadCsv() throws IOException {
    groupRegionMap = new LinkedHashMap<>();
    globalRegionMap = new LinkedHashMap<>();
    //ReadGlobalRegion.readRegionToMap(this.globalRegionFile,groupRegionMap,globalRegionMap);
  }

  private void loadTxt() throws Exception {
    ByteArrayOutputStream out = null;
    FileInputStream in = null;
    lock.lock();
    try {
      out = new ByteArrayOutputStream();
      byte[] b = new byte[1024];
      in = new FileInputStream(qqwryFile);
      while (in.read(b) != -1) {
        out.write(b);
      }
      data = out.toByteArray();
      firstIndexOffset = read4ByteAsLong(0);
      lastIndexOffset = read4ByteAsLong(4);
      totalIndexCount = (lastIndexOffset - firstIndexOffset) / IP_RECORD_LENGTH + 1;
      in.close();
      out.close();
    } finally {
      try {
        if (out != null) {
          out.close();
        }
        if (in != null) {
          in.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
      lock.unlock();
    }
  }

  private long read4ByteAsLong(final int offset) {
    long val = data[offset] & 0xFF;
    val |= (data[offset + 1] << 8L) & 0xFF00L;
    val |= (data[offset + 2] << 16L) & 0xFF0000L;
    val |= (data[offset + 3] << 24L) & 0xFF000000L;
    return val;
  }

  public void trans2Txt(String txtPath) {
    lock.lock();
    try {
      BufferedWriter out = new BufferedWriter(new FileWriter(txtPath));
      execute(out);
      out.close();
    } catch (Exception e){
      e.printStackTrace();
    }finally {
      lock.unlock();
    }
  }

  private void execute(BufferedWriter out) {
    long low = 0;
    long high = totalIndexCount;
    long mid = 0;
    while (low <= high) {
      mid = low;
      long indexIP = read4ByteAsLong((int) (firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH));
      long indexIPNext = read4ByteAsLong((int) (firstIndexOffset + mid * IP_RECORD_LENGTH));
      readIPLocation(indexIP,indexIPNext,
          (int) read3ByteAsLong((int) (firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 4)),
          (int) read3ByteAsLong((int) (firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 7)),
          out);
      low++;
    }
  }

  private void readIPLocation(final long indexIP, final long indexIPNext, final int offset1,
      final int offset2, BufferedWriter out) {
    try {
      out.write(indexIP
          +"|"+indexIPNext
          +"|"+readString(offset1)
          +"|"+readString(offset2));
      out.newLine();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private String getLocationByPid(String pid){
    String[] p = globalRegionMap.get(pid);
    if(null != p){
      return p[2];
    }
    return unknowStr;
  }

  private String[] getLocationsByPid(String pid){
    String[] p = globalRegionMap.get(pid);
    if(null != p){
      return p;
    }
    return unknowEntry.getValue();
  }


  private long read3ByteAsLong(final int offset) {
    long val = data[offset] & 0xFF;
    val |= (data[offset + 1] << 8) & 0xFF00;
    val |= (data[offset + 2] << 16) & 0xFF0000;
    return val;
  }

  private String readString(int offset) {
    final byte[] b = new byte[128];
    int i;
    try {
      for (i = 0, b[i] = data[offset++]; b[i] != 0; b[++i] = data[offset++]){}
      return new String(b, 0, i, "GBk");
    } catch (Exception e) {
      return new String("");
    }
  }
  /**
   * @Description:将long类型的ip转换为“.”分隔的ip串
   * @time:2016年11月23日 下午4:41:11
   * @param ip
   * @return
   * @return:String
   */
  public static String  inet_ntoa(long ip){
    String fmtIp = "";
    fmtIp += String.valueOf(ip >> 24 & 0xff).concat(".");
    fmtIp += String.valueOf(ip >> 16 & 0xff).concat(".");
    fmtIp += String.valueOf(ip >> 8 & 0xff).concat(".");
    fmtIp += String.valueOf(ip & 0xff);
    return fmtIp;
  }
}
